#pragma once
#include <iostream>
#include <queue>
#include "LockGuard.hpp"
using namespace std;

const int default_capacity = 5;

template <typename T>
class BlockQueue
{
    queue<T> _q;
    int _capacity;
    pthread_mutex_t _mutex;
    pthread_cond_t _pro_cond; // 给生产者
    pthread_cond_t _con_cond; // 给消费者
public:
    // BlockQueue() {}
    BlockQueue(int _capacity = default_capacity) : _capacity(_capacity)
    {
        pthread_mutex_init(&_mutex, nullptr);   // 局部锁需要init函数初始化
        pthread_cond_init(&_pro_cond, nullptr); // 局部条件变量需要init函数初始化
        pthread_cond_init(&_con_cond, nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);   // 局部锁需要销毁
        pthread_cond_destroy(&_pro_cond); // 局部条件变量需要销毁
        pthread_cond_destroy(&_con_cond);
    }

    bool IsFull()
    {
        return _q.size() == _capacity;
    }

    bool IsEmpty()
    {
        return _q.size() == 0;
    }

    bool Push(const T &in)
    {
        // pthread_mutex_lock(&_mutex);
        {
            LockGuard lockguard(&_mutex);
            while (IsFull()) // 解决伪唤醒的问题
            {
                // 满了需要阻塞等待
                pthread_cond_wait(&_pro_cond, &_mutex);
            }
            // 没满，可以生产加入
            _q.push(in);
            pthread_cond_signal(&_con_cond); // 生产一个,通知消费者消费
            // 线程唤醒操作，既可以放在解锁内也可以放在解锁外
            // 放在解锁内，线程被唤醒需要等待当前线程释放锁
        }
        // pthread_mutex_unlock(&_mutex);
        return true;
    }

    bool Pop(T *out)
    {
        // pthread_mutex_lock(&_mutex);
        {
            LockGuard lockguard(&_mutex);
            while (IsEmpty()) // 解决伪唤醒的问题
            {
                // 队列为空,消费需要等待
                pthread_cond_wait(&_con_cond, &_mutex);
            }
            *out = _q.front();
            _q.pop();
            pthread_cond_signal(&_pro_cond); // 消费一个,通知生产者生产
        }
        // pthread_mutex_unlock(&_mutex);
        return true;
    }
};